package com.lcm.weam.util;

import java.math.BigDecimal;
import java.math.RoundingMode;
import java.util.Arrays;

/**
 * 加权Markov工具类 (长度为n >= 2)
 *
 */
public class MarkovUtil {

    //数据分类状态数
    private static int n;

    public static void setN(int n) {
        MarkovUtil.n = n;
    }

    //求数组均值
    public static double mean(double[] data){
        double sum = 0.0;
        for (double datum : data) {
            sum += datum;
        }
        return sum / data.length;
    }

    //求数组方差
    private static double square(double[] data, double mean){
        double sum = 0.0;
        for (double datum : data) {
            sum += Math.pow((datum - mean), 2);
        }
        return sum;
    }

    //求数组均方差无偏估计值
    public static double meanSquare(double[] data, double mean){
        double sum = 0.0;
        for (double datum : data) {
            sum += Math.pow((datum - mean), 2);
        }
        return Math.pow(sum / (data.length - 1), 0.5);
    }

    //均方差法分n个状态，对应n-1个分级点
    public static double[] getLimits(double mean, double meanSquare){
        double[] limits = new double[n-1];
        double v = 1.0; //计算分级系数（前提：n > 2,否则为1，即分界点为均值）
        double nd = n;
        if (n > 2){ //n值需要由前端限制取值3-10，BigDecimal为了四舍五入取两位有效小数
            v = BigDecimal.valueOf(2 / (nd - 2))
                .setScale(2, RoundingMode.HALF_UP).doubleValue();
        }
        //计算每个分级节点值
        for (int i = 0; i < n - 1; i++) {
            limits[i] = mean + (i * v - 1) * meanSquare;
        }
        //返回结果
        return limits;
    }

    //返回double[数据][状态]
    public static double[] getDataState(double[] data, double[] limits){
        double[] res = new double[data.length];
        for (int i = 0; i < data.length; i++) {
            res[i] = isState(data[i], limits);
        }
        return res;
    }

    //判断状态
    private static double isState(double data, double[] limits){
        //状态数 = limits长度 + 1
        for (int i = 0; i < limits.length; i++) {
            if (data <= limits[i]){
                return i + 1;
            }
        }
        return limits.length + 1;
    }

    //获取对应步长step转移矩阵 transferMatrix n*n
    public static double[][] getTransferMatrix(double[] dataState, int k){
        double[][] res = new double[n][n];
        for (int i = 0; i < dataState.length - k; i++) {
            int state = (int)dataState[i]; //当前状态
            int nextState = (int)dataState[i + k] - 1; //滞时状态 -1
            //判断状态为转移矩阵赋值
            for (int j = 0; j < n; j++) {
                if (state == (j + 1)){
                    res[j][nextState] += 1;
                }
            }
        }
        return res;
    }

    //输出马氏性检验结果
    public static double isUseful(double[][] transferMatrix){
        double[][] pMatrix = getPMatrix(transferMatrix); //pij
        double[] jpMatrix = getJPMatrix(transferMatrix); //pj
        double x2 = 0.0;
        for (int i = 0; i < n; i++) {
            for (int j = 0; j < n; j++) {
                if (pMatrix[i][j] != 0.0 && jpMatrix[j] != 0.0){
                    x2 += 2 * transferMatrix[i][j] * Math.abs( Math.log( pMatrix[i][j] / jpMatrix[j] ) );
                }
            }
        }
        return x2;
    }

    //获取转移概率矩阵PMatrix n*n
    public static double[][] getPMatrix(double[][] transferMatrix){
//        int m = transferMatrix.length; //m=5
        double[][] res = new double[n][n];
        for (int i = 0; i < n; i++) {
            double sum = 0.0;
            for (int j = 0; j < n; j++) {
                sum += transferMatrix[i][j];
            }
            for (int j = 0; j < n; j++) {
                if (sum == 0.0){
                    res[i][j] = 0.0;
                }else{
                    res[i][j] = transferMatrix[i][j] / sum;
                }
            }
        }
        return res;
    }

    //获取转移边际概率矩阵n*n
    private static double[] getJPMatrix(double[][] transferMatrix){
        double sum = 0.0;
//        int m = transferMatrix.length; //m=5
        double[] colsum = new double[n];
        for (int i = 0; i < n; i++) {
            for (int j = 0; j < n; j++) {
                colsum[i] += transferMatrix[j][i];
            }
            sum += colsum[i];
        }
        double[] res = new double[n];
        for (int i = 0; i < n; i++) {
            res[i] = colsum[i] / sum;
        }
        return res;
    }

    //计算某一阶自相关系数rk: 滞时为k时，每个时段的自相关系数
    public static double getRk(double[] data, double mean, int k){
        int m = data.length;
        assert m > k;
        double down = square(data, mean);
        double up = 0.0;
        for (int i = 0; i < m - k; i++) {
            up += (data[i] - mean) * (data[i + k] - mean);
        }
        return up / down;
    }

    //计算步长权重，参数是从小到大步长的自相关系数数组
    public static double[] getWeight(double[] rk){
        double[] res = new double[rk.length];
        double sum = 0.0;
        for (double v : rk) {
            sum += Math.abs(v);
        }
        for (int i = 0; i < rk.length; i++) {
            res[i] = Math.abs(rk[i]) / sum;
        }
        return res;
    }

    //计算级别特征值得到预测值(使用上下限均值)
    public static double getValue(double[] p, double[] limit, double[] data){
        //获取最大值的状态[1,2,3,4,5]
        int maxState = 1;
        for (int i = 1; i < p.length; i++) {
            if (p[i] > p[maxState - 1]){
                maxState = i + 1;
            }
        }
        //判断上下限
        double T = 0.0; //上限
        double B = 0.0; //下限
        double max = Arrays.stream(data).max().getAsDouble();
        double min = Arrays.stream(data).min().getAsDouble();
        //上下限赋值
        if (maxState == 1) {
            T = limit[0];
            B = min;
        }
        for (int i = 1; i < n - 1; i++) {
            if (maxState == (i + 1)){
                T = limit[i];
                B = limit[i-1];
            }
        }
        if (maxState == n){
            T = max;
            B = limit[n-2];
        }
        return (T + B) / 2;
    }
}
